#include "lightfrustum.h"
#include "scene.h"
#include "textures.h"

using namespace Scene;
using namespace Textures;

namespace LightFrustum
{

void enable_texgen(matrix44 mProj)
{
	vector4 v[4];
	v[0] = vector4(1,0,0,0);
	v[1] = vector4(0,1,0,0);
	v[2] = vector4(0,0,1,0);
	v[3] = vector4(0,0,0,1);		
	glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
	glTexGenfv(GL_S, GL_OBJECT_PLANE, (float*)&(v[0]));
	glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
	glTexGenfv(GL_T, GL_OBJECT_PLANE, (float*)&(v[1]));
	glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
	glTexGenfv(GL_R, GL_OBJECT_PLANE, (float*)&(v[2]));
	glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
	glTexGenfv(GL_Q, GL_OBJECT_PLANE, (float*)&(v[3]));
	glEnable(GL_TEXTURE_GEN_S);
	glEnable(GL_TEXTURE_GEN_T);
	glEnable(GL_TEXTURE_GEN_R);
	glEnable(GL_TEXTURE_GEN_Q);
	glMatrixMode(GL_TEXTURE);
	glLoadMatrixf((float*)&(mProj));
	glMatrixMode(GL_MODELVIEW);
}


void disable_texgen()
{
	glDisable(GL_TEXTURE_GEN_S);
	glDisable(GL_TEXTURE_GEN_T);
	glDisable(GL_TEXTURE_GEN_R);
	glDisable(GL_TEXTURE_GEN_Q);
	glMatrixMode(GL_TEXTURE);
	glLoadIdentity();
	glMatrixMode(GL_MODELVIEW);
}

void enable_clip_plane(int i, vector3 p0, vector3 p1, vector3 p2, bool reflection)
{
	vector3 n = CrossProduct(p1-p0,p2-p0).normalize();
	double cp[4] = {-n.x, -n.y, -n.z, DotProduct(n,p0)};
	if (reflection)
	{
		cp[0] *= -1.0f;
		cp[1] *= -1.0f;
		cp[2] *= -1.0f;
		cp[3] *= -1.0f;
	}
	glEnable(GL_CLIP_PLANE0+i);
	glClipPlane(GL_CLIP_PLANE0+i, cp);
}

void draw_quad(const vector3 &p0, const vector3 &p1, const vector3 &p2, const vector3 &p3)
{
	vector3 n = CrossProduct(p1-p0,p2-p0).normalize();
	glNormal3f(n.x,n.y,n.z);
	glVertex3f(p0.x,p0.y,p0.z);
	glVertex3f(p1.x,p1.y,p1.z);
	glVertex3f(p2.x,p2.y,p2.z);
	glVertex3f(p3.x,p3.y,p3.z);
}

void light_texture_operation()
{
	glActiveTexture(GL_TEXTURE0);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
	glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
	glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS);
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
	glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
	glActiveTexture(GL_TEXTURE1);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
	glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
	glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS);
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
	glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
}

void render(bool reflection)
{
	glPushMatrix();
	glLoadIdentity();

	int i;
	vector4 p[8];
	p[0] = light_near * vector4(-1.0f,	-1.0f,	-1.0f, 1.0f);
	p[1] = light_near * vector4(1.0f,	-1.0f,	-1.0f, 1.0f);
	p[2] = light_near * vector4(1.0f,	1.0f,	-1.0f, 1.0f);
	p[3] = light_near * vector4(-1.0f,	1.0f,	-1.0f, 1.0f);
	p[4] = light_far * vector4(-1.0f,	-1.0f,	1.0f, 1.0f);
	p[5] = light_far * vector4(1.0f,	-1.0f,	1.0f, 1.0f);
	p[6] = light_far * vector4(1.0f,	1.0f,	1.0f, 1.0f);
	p[7] = light_far * vector4(-1.0f,	1.0f,	1.0f, 1.0f);
	vector3 pp[8];
	matrix44 matrix_light_inverse = matrix_lamp_projection * matrix_lamp_view;
	matrix_light_inverse.invert();
	for (i = 0; i < 8; ++i)
	{
		vector4 result = matrix_light_inverse * p[i];
		result.w = 1.0f;
		result = matView * result;
		pp[i] = vector3(result.x, result.y, result.z);
	}
/*
	glColor3f(.0f,.0f,1.0f);
	glBegin(GL_QUADS);
		draw_quad(pp[0],pp[1],pp[2],pp[3]);
		draw_quad(pp[4],pp[7],pp[6],pp[5]);
		draw_quad(pp[0],pp[4],pp[5],pp[1]);
		draw_quad(pp[1],pp[5],pp[6],pp[2]);
		draw_quad(pp[2],pp[6],pp[7],pp[3]);
		draw_quad(pp[3],pp[7],pp[4],pp[0]);
	glEnd();
	*/
	
	enable_clip_plane(1, pp[0],pp[1],pp[2], reflection);
//	enable_clip_plane(1, pp[4],pp[7],pp[6], reflection);
	enable_clip_plane(2, pp[0],pp[4],pp[5], reflection);
	enable_clip_plane(3, pp[1],pp[5],pp[6], reflection);
	enable_clip_plane(4, pp[2],pp[6],pp[7], reflection);
	enable_clip_plane(5, pp[3],pp[7],pp[4], reflection);

	vector3 vmin, vmax;
	vmin = vmax = pp[0];
	for (i = 1; i < 8; ++i)
	{
		if (pp[i].x < vmin.x)
			vmin.x = pp[i].x;
		if (pp[i].x > vmax.x)
			vmax.x = pp[i].x;
		if (pp[i].y < vmin.y)
			vmin.y = pp[i].y;
		if (pp[i].y > vmax.y)
			vmax.y = pp[i].y;
		if (pp[i].z < vmin.z)
			vmin.z = pp[i].z;
		if (pp[i].z > vmax.z)
			vmax.z = pp[i].z;
	}

	glPushAttrib(GL_ALL_ATTRIB_BITS);

	matrix44 mproj = matView;
	mproj.invert();
	mproj = TranslateMatrix44(0.5f, 0.5f, 0.5f) * ScaleMatrix44(0.5f, 0.5f, 0.5f) * matrix_lamp_projection * matrix_lamp_view * mproj;
	glActiveTexture(GL_TEXTURE0);
	enable_texgen(mproj);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, tex_light_cookie);
	glActiveTexture(GL_TEXTURE1);
	matrix44 mexchange = IdentityMatrix44();
	mexchange.col[0].x = 0.0f;
	mexchange.col[2].x = 1.0f;
	mexchange.col[1].y = 0.0f;
	enable_texgen(mexchange*mproj);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, tex_light_att);
	light_texture_operation();
	glDepthMask(GL_FALSE);
	glColor4f(1.0f,0.9f,0.6f,.15f);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE);
	
	const int max_planes = 32;
	glDisable(GL_LIGHTING);
	glFrontFace(GL_CCW);
	glBegin(GL_QUADS);
	for (i = 0; i < max_planes; ++i)
	{
		float z = (static_cast<float>(i)+0.5f)/static_cast<float>(max_planes);
		z = vmin.z+z*(vmax.z-vmin.z);
		glNormal3f(.0f,.0f,1.0f);
		glVertex3f(vmin.x, vmin.y, z);
		glVertex3f(vmax.x, vmin.y, z);
		glVertex3f(vmax.x, vmax.y, z);
		glVertex3f(vmin.x, vmax.y, z);
	}
	glEnd();
	glEnable(GL_LIGHTING);
	glDisable(GL_BLEND);
	glActiveTexture(GL_TEXTURE0);
	glDisable(GL_TEXTURE_2D);
	disable_texgen();
	glActiveTexture(GL_TEXTURE1);
	glDisable(GL_TEXTURE_2D);
	disable_texgen();
	glPopAttrib();

	for (i = 1; i < 6; ++i)
		glDisable(GL_CLIP_PLANE0+i);

	glPopMatrix();
}

}